package com.fangcang.b2b.wxpay.controller;

import com.alibaba.fastjson.JSON;
import com.fangcang.b2b.common.controller.BaseController;
import com.fangcang.b2b.wxpay.domain.WxPayNotifyDO;
import com.fangcang.b2b.wxpay.domain.WxPayRecordDO;
import com.fangcang.b2b.wxpay.dto.WxPrepayRequestDTO;
import com.fangcang.b2b.wxpay.dto.WxPrepayResponseDTO;
import com.fangcang.b2b.wxpay.enums.WxPayConstants;
import com.fangcang.b2b.wxpay.enums.WxPayNotifyStatusEnum;
import com.fangcang.b2b.wxpay.enums.WxPayRecordStatusEnum;
import com.fangcang.b2b.wxpay.service.WxPayService;
import com.fangcang.b2b.wxpay.util.CommonUtil;
import com.fangcang.b2b.wxpay.util.IPUtil;
import com.fangcang.b2b.wxpay.util.PayCommonUtil;
import com.fangcang.b2b.wxpay.util.WxPayUtil;
import com.fangcang.common.ResponseDTO;
import com.fangcang.common.enums.CurrencyEnum;
import com.fangcang.common.enums.ResultCodeEnum;
import com.fangcang.common.enums.order.OrderStatusEnum;
import com.fangcang.common.enums.order.PayStatusEnum;
import com.fangcang.common.util.DateUtil;
import com.fangcang.common.util.StringUtil;
import com.fangcang.finance.enums.FinanceStatusEnum;
import com.fangcang.finance.enums.FinanceTypeEnum;
import com.fangcang.finance.enums.PassageEnum;
import com.fangcang.finance.financeorder.request.ConfirmPayRequestDTO;
import com.fangcang.finance.financeorder.request.VoucherRequestDTO;
import com.fangcang.finance.financeorder.service.AgentFinanceOrderService;
import com.fangcang.order.request.OrderDetailRequestDTO;
import com.fangcang.order.response.OrderDetailResponseDTO;
import com.fangcang.order.service.OrderDetailService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/wxpay")
public class WxPayController extends BaseController {

    @Autowired
    private WxPayService wxPayService;

    @Autowired
    private OrderDetailService orderDetailService;

    @Autowired
    private AgentFinanceOrderService agentFinanceOrderService;

    /**
     * 微信支付页面同步通知地址
     */
    public String WXPAY_CALLBACK_URL = "wxpay/wxPaySuccess.shtml";

    /**
     * 微信支付页面异步通知地址
     */
    public String WXPAY_NOTIFY_URL = "/b2b-app/wxpay/wxPayNotify.shtml";

    /**
     * 获取微信支付参数
     */
    @RequestMapping(value = "/wxPay/{orderCode}", method = RequestMethod.POST, produces = { "application/json;charset=UTF-8" })
    @ResponseBody
    public ResponseDTO wxPay(@PathVariable("orderCode") String orderCode){
        ResponseDTO response=new ResponseDTO(ResultCodeEnum.SUCCESS.code);
        Long t1 = System.currentTimeMillis();
        Long t2 = null;
        Long t3 = null;
        Long t4 = null;
        Long t5 = null;
        Long t6 = null;
        //1.检查参数
        if (!StringUtil.isValidString(orderCode)){
            response.setResult(ResultCodeEnum.FAILURE.code);
            response.setFailReason("订单号不能为空");
            return response;
        }
        //2、检查微信版本是否支持微信支付
        String errorMsg = checkWechatVersion();
        if (StringUtil.isValidString(errorMsg)) {
            response.setResult(ResultCodeEnum.FAILURE.code);
            response.setFailReason(errorMsg);
            return response;
        }
        t2 = System.currentTimeMillis();

        try{
            //3，查询该订单是否已支付
            OrderDetailRequestDTO orderDetailRequestDTO = new OrderDetailRequestDTO();
            orderDetailRequestDTO.setOrderCode(orderCode);
            ResponseDTO<OrderDetailResponseDTO> orderDetailResponseDTO = orderDetailService.orderInfo(orderDetailRequestDTO);
            if(null == orderDetailResponseDTO || ResultCodeEnum.FAILURE.code == orderDetailResponseDTO.getResult() || null == orderDetailResponseDTO.getModel()){
                response.setResult(ResultCodeEnum.FAILURE.code);
                response.setFailReason(null == orderDetailResponseDTO ? "查询订单详情失败" : orderDetailResponseDTO.getFailReason());
                return response;
            }
            t3 = System.currentTimeMillis();

            OrderDetailResponseDTO orderDetail = orderDetailResponseDTO.getModel();
            BigDecimal orderSum= orderDetail.getOrderSum();
            BigDecimal unpaidAmount = orderSum;
            List<WxPayRecordDO> list=wxPayService.queryWxPayRecord(orderCode, WxPayRecordStatusEnum.PAY.code);
            if (list!=null && list.size()>0){
                BigDecimal payAmount=BigDecimal.ZERO;
                for (WxPayRecordDO wxPayRecordDO:list){
                    payAmount=payAmount.add(wxPayRecordDO.getPayAmount());
                }
                if (orderSum.compareTo(payAmount)<=0){
                    response.setResult(ResultCodeEnum.FAILURE.code);
                    response.setFailReason("订单已支付");
                    return response;
                }
                unpaidAmount = orderSum.subtract(payAmount);
            }
            t4 = System.currentTimeMillis();

            //4，调微信接口下预付单，并得到微信支付参数
            WxPrepayRequestDTO requestDTO=new WxPrepayRequestDTO();
            requestDTO.setOpenId(super.getCacheVisitWxOpenId());
            requestDTO.setOrderCode(orderDetail.getOrderCode());
            StringBuilder productName = new StringBuilder();
            productName.append(orderDetail.getHotelName()).append("(").append(orderDetail.getRoomTypeNames()).append(")")
                    .append("(").append(orderDetail.getRateplanName()).append(")");
            requestDTO.setProductName(productName.toString());
            requestDTO.setOrderAmount(unpaidAmount);
            requestDTO.setNotifyUrl("http://"+this.getRequest().getServerName()+WXPAY_NOTIFY_URL);
            requestDTO.setClientIp(IPUtil.getIP(getRequest()));
            requestDTO.setCreator(super.getOperator());
            WxPrepayResponseDTO responseDTO=wxPayService.createWechatPrepayInfo(requestDTO);
            response.setModel(responseDTO.getWeiXinH5PayVo());

            t5 = System.currentTimeMillis();

            //5、客户单号跟微信prepayId 绑定插入微信支付记录表
            WxPayRecordDO wxPayRecordDO=new WxPayRecordDO();
            wxPayRecordDO.setOrderCode(orderCode);
            wxPayRecordDO.setPayAmount(unpaidAmount);
            wxPayRecordDO.setWeixinOutTradeNo(responseDTO.getWeixinOutTradeNo());
            wxPayRecordDO.setPrepayId(responseDTO.getPerpayId());
            wxPayRecordDO.setStatus(WxPayRecordStatusEnum.NEW.code);
            wxPayRecordDO.setCreator(super.getOperator());
            wxPayRecordDO.setCreateTime(new Date());
            wxPayService.saveWxPayRecord(wxPayRecordDO);

            t6 = System.currentTimeMillis();

            log.info("checkWechatVersion cost:" + (t2 -t1) + "ms," +
                    "orderInfo cost:" + (t3 - t2) + "ms," +
                    "queryWxPayRecord cost:" + (t4 -t3) + "ms," +
                    "createWechatPrepayInfo cost:" + (t5 -t4) + "ms," +
                    "saveWxPayRecord cost:" + (t6 -t5) + "ms," +
                    "total cost:" + (t6 - t1) + "ms");
            return response;
        }catch (Exception e){
            log.error("下预付单失败：",e);
            response.setResult(ResultCodeEnum.FAILURE.code);
            response.setFailReason("系统异常");
            return response;
        }
    }

    /**
     * 微信支付成功（同步跳转）
     */
    @RequestMapping(value="/wxPaySuccess.shtml", method= RequestMethod.GET)
    public void wxPaySuccess(){

    }

    /**
     * 微信支付结果通知（异步通知）
     */
    @RequestMapping(value="/wxPayNotify.shtml")
    public void wxPayNotify(HttpServletResponse httpServletResponse) throws IOException {
        log.info("************ begin WxPayController.wxPayNotify ************");

        String errorMsg = "";//错误原因
        int status= WxPayNotifyStatusEnum.FAILURE.code;//默认状态-失败
        WxPayNotifyDO wxPayNotifyDO=new WxPayNotifyDO();//保存微信支付通知记录obj

        //1、获取微信回调信息
        String result = "";//支付结果通知原版内容
        String transactionId = "";//微信支付订单号
        InputStream requestStream = getRequest().getInputStream();
        if (requestStream != null) {
            result = IOUtils.toString(requestStream, "UTF-8");
            log.info("异步微信支付结果通知原版内容：" + result);
        } else {
            errorMsg = "参数格式校验错误(requestStream=null)";
            //添加微信支付通知记录，失败添加描述
            wxPayNotifyDO.setNoticeComment(result);
            wxPayNotifyDO.setWeixinTransactionId(transactionId);
            wxPayNotifyDO.setStatus(WxPayNotifyStatusEnum.FAILURE.code);
            wxPayNotifyDO.setResultDesc(errorMsg);
            wxPayNotifyDO.setCreateTime(new Date());
            wxPayService.saveWxPayNotify(wxPayNotifyDO);

            getResponse().getWriter().write(PayCommonUtil.setXML("FAIL", errorMsg));
            log.error("异步微信支付结果通知失败：" + errorMsg);
            return;
        }

        if (StringUtils.isBlank(result)) {
            errorMsg = "参数格式校验错误(result)";
            //添加微信支付通知记录，失败添加描述
            wxPayNotifyDO.setNoticeComment(result);
            wxPayNotifyDO.setWeixinTransactionId(transactionId);
            wxPayNotifyDO.setStatus(WxPayNotifyStatusEnum.FAILURE.code);
            wxPayNotifyDO.setResultDesc(errorMsg);
            wxPayNotifyDO.setCreateTime(new Date());
            wxPayService.saveWxPayNotify(wxPayNotifyDO);

            getResponse().getWriter().write(PayCommonUtil.setXML("FAIL", errorMsg));
            log.error("异步微信支付结果通知失败：" + errorMsg);
            return;
        }

        //2、转换微信消息
        Map<String, String> noticeDatas = null;
        try{
            noticeDatas = WxPayUtil.xmlToMap(result);
        }catch(Exception e){
            errorMsg = "参数格式校验错误(转换微信消息)";
            //添加微信支付通知记录，失败添加描述
            wxPayNotifyDO.setNoticeComment(result);
            wxPayNotifyDO.setWeixinTransactionId(transactionId);
            wxPayNotifyDO.setStatus(WxPayNotifyStatusEnum.FAILURE.code);
            wxPayNotifyDO.setResultDesc(errorMsg);
            wxPayNotifyDO.setCreateTime(new Date());
            wxPayService.saveWxPayNotify(wxPayNotifyDO);

            getResponse().getWriter().write(PayCommonUtil.setXML("FAIL", errorMsg));
            log.error("异步微信支付结果通知失败：" + errorMsg);
            return;
        }

        String tradeNo = "";	//交易号=客户单号
        String openId = "";		//微信openid

        //3、解析微信消息
        if(noticeDatas != null && WxPayConstants.SUCCESS.equalsIgnoreCase(noticeDatas.get("return_code"))){
            String attach = noticeDatas.get("attach");
            if (WxPayConstants.SUCCESS.equalsIgnoreCase(noticeDatas.get("result_code"))){
                //4、解析附加数据
                transactionId = noticeDatas.get("transaction_id");//微信支付订单号
                if (StringUtils.isNotBlank(attach)) {
                    String attchArr[] = attach.split(",");
                    wxPayNotifyDO.setOrderCode(attchArr[0]);
                    wxPayNotifyDO.setWeixinOutTradeNo(attchArr[1]);
                    wxPayNotifyDO.setCreator(attchArr[2]);
                } else {
                    errorMsg = "解析附加数据失败";
                    //添加微信支付通知记录，失败添加描述
                    wxPayNotifyDO.setNoticeComment(result);
                    wxPayNotifyDO.setWeixinTransactionId(transactionId);
                    wxPayNotifyDO.setStatus(WxPayNotifyStatusEnum.FAILURE.code);
                    wxPayNotifyDO.setResultDesc(errorMsg);
                    wxPayNotifyDO.setCreateTime(new Date());
                    wxPayService.saveWxPayNotify(wxPayNotifyDO);

                    getResponse().getWriter().write(PayCommonUtil.setXML("FAIL", errorMsg));
                    log.error("异步微信支付结果通知失败:"+errorMsg);
                    return;
                }

                tradeNo = noticeDatas.get("out_trade_no");	//交易号（酒店/DMS为工单，预售为订单加随机数）
                openId = noticeDatas.get("openid");	//微信openid

                //5、签名验证
                try{
                    boolean verifyResult = WxPayUtil.isPayResultNotifySignatureValid(noticeDatas, wxPayService.queryWxPayConfig().getApiKey());
                    if (!verifyResult) {
                        errorMsg = "签名认证失败";
                        //添加微信支付通知记录，失败添加描述
                        wxPayNotifyDO.setNoticeComment(result);
                        wxPayNotifyDO.setWeixinTransactionId(transactionId);
                        wxPayNotifyDO.setStatus(WxPayNotifyStatusEnum.FAILURE.code);
                        wxPayNotifyDO.setResultDesc(errorMsg);
                        wxPayNotifyDO.setCreateTime(new Date());
                        wxPayService.saveWxPayNotify(wxPayNotifyDO);

                        getResponse().getWriter().write(PayCommonUtil.setXML("FAIL", errorMsg));
                        log.error("异步微信支付结果通知失败，签名认证失败！\n："+result);
                        return;
                    }else{
                        log.info("异步微信支付签名认证通过！\n："+result);
                    }
                }catch(Exception e){
                    errorMsg="签名认证失败，发生异常";
                    //添加微信支付通知记录，失败添加描述
                    wxPayNotifyDO.setNoticeComment(result);
                    wxPayNotifyDO.setWeixinTransactionId(transactionId);
                    wxPayNotifyDO.setStatus(WxPayNotifyStatusEnum.FAILURE.code);
                    wxPayNotifyDO.setResultDesc(errorMsg);
                    wxPayNotifyDO.setCreateTime(new Date());
                    wxPayService.saveWxPayNotify(wxPayNotifyDO);

                    log.error("异步微信支付结果通知失败，签名认证失败，发生异常！\n："+result, e);
                }

                //6、添加微信支付通知记录，失败添加描述
                wxPayNotifyDO.setNoticeComment(result);
                wxPayNotifyDO.setWeixinTransactionId(transactionId);
                wxPayNotifyDO.setCreateTime(new Date());
                wxPayService.saveWxPayNotify(wxPayNotifyDO);

                //7.查询订单信息
                ResponseDTO<OrderDetailResponseDTO> responseDTO = null;
                try {
                    OrderDetailRequestDTO orderDetailRequestDTO = new OrderDetailRequestDTO();
                    orderDetailRequestDTO.setOrderCode(wxPayNotifyDO.getOrderCode());
                    responseDTO = orderDetailService.orderInfo(orderDetailRequestDTO);
                } catch (Exception e) {
                    errorMsg = "查询订单异常";
                    wxPayService.updateWxPayNotifyStatus(tradeNo,status,errorMsg);
                    getResponse().getWriter().write(PayCommonUtil.setXML("FAIL", errorMsg));
                    log.error("查询订单异常:" + wxPayNotifyDO.getOrderCode());
                    return;
                }
                if (responseDTO == null || ResultCodeEnum.FAILURE.code == responseDTO.getResult() || null == responseDTO.getModel()){
                    errorMsg = "查询订单异常";
                    wxPayService.updateWxPayNotifyStatus(tradeNo,status,errorMsg);
                    getResponse().getWriter().write(PayCommonUtil.setXML("FAIL", errorMsg));
                    log.error("查询订单异常" + wxPayNotifyDO.getOrderCode());
                    return;
                }
                OrderDetailResponseDTO orderDetailResponseDTO = responseDTO.getModel();

                if (orderDetailResponseDTO.getOrderStatus()== OrderStatusEnum.CANCELED.key){
                    //返回应答
                    errorMsg = "订单状态异常(订单已取消)";
                    wxPayService.updateWxPayNotifyStatus(tradeNo,status,errorMsg);
                    getResponse().getWriter().write(PayCommonUtil.setXML("FAIL", errorMsg));
                    log.error("订单状态异常(订单已取消)" + wxPayNotifyDO.getOrderCode());
                    return;
                }else if(orderDetailResponseDTO.getPayStatus() == PayStatusEnum.PAID.key){
                    status = WxPayNotifyStatusEnum.SUCCESS.code;
                    errorMsg="订单已支付";
                    wxPayService.updateWxPayNotifyStatus(tradeNo,status,errorMsg);
                    //返回应答
                    getResponse().getWriter().write(PayCommonUtil.setXML("SUCCESS", "OK"));
                    log.info("订单已支付" + tradeNo);
                    return;
                }

                //8.确认支付
                try {
                    ConfirmPayRequestDTO confirmPayRequestDTO = new ConfirmPayRequestDTO();
                    confirmPayRequestDTO.setFinanceStatus(FinanceStatusEnum.CHECKOUT.key);
                    confirmPayRequestDTO.setOrderCode(wxPayNotifyDO.getOrderCode());
                    confirmPayRequestDTO.setOrgCode(orderDetailResponseDTO.getAgentCode());
                    confirmPayRequestDTO.setOrgName(orderDetailResponseDTO.getAgentName());
                    confirmPayRequestDTO.setMerchantCode(orderDetailResponseDTO.getMerchantCode());
                    confirmPayRequestDTO.setCurrency(CurrencyEnum.CNY.value);
                    BigDecimal orderPayMount = new BigDecimal(noticeDatas.get("total_fee")).divide(new BigDecimal(100));//订单总金额，单位为分
                    confirmPayRequestDTO.setNotifyAmount(orderPayMount);
                    confirmPayRequestDTO.setPassage(PassageEnum.WXPAY.key);
                    confirmPayRequestDTO.setFinanceType(FinanceTypeEnum.ORDERPAY.key);
                    confirmPayRequestDTO.setCreator(wxPayNotifyDO.getCreator());
                    VoucherRequestDTO voucherDTO=new VoucherRequestDTO();
                    voucherDTO.setPassage(PassageEnum.WXPAY.key);
                    voucherDTO.setAmount(orderPayMount);
                    voucherDTO.setCurrency(CurrencyEnum.CNY.value);
                    voucherDTO.setArrivalDate(DateUtil.dateToStringWithHms(new Date()));
                    confirmPayRequestDTO.setVoucherList(Arrays.asList(voucherDTO));

                    //转账记录
                    List<VoucherRequestDTO> voucherList = new ArrayList<>();
                    VoucherRequestDTO voucherRequestDTO = new VoucherRequestDTO();
                    voucherRequestDTO.setPassage(PassageEnum.WXPAY.key);
                    voucherRequestDTO.setAmount(orderPayMount);
                    voucherRequestDTO.setCurrency(CurrencyEnum.CNY.value);
                    voucherList.add(voucherRequestDTO);
                    confirmPayRequestDTO.setVoucherList(voucherList);

                    log.info("confirmPay:" + JSON.toJSONString(confirmPayRequestDTO));
                    ResponseDTO<Integer> confirmPayResponse = agentFinanceOrderService.confirmPay(confirmPayRequestDTO);
                    if(null == confirmPayResponse || ResultCodeEnum.FAILURE.code == confirmPayResponse.getResult()
                            || null == confirmPayResponse.getModel()){
                        errorMsg = "更新订单支付状态返回空";
                        wxPayService.updateWxPayNotifyStatus(tradeNo,status,errorMsg);
                        getResponse().getWriter().write(PayCommonUtil.setXML("FAIL", errorMsg));
                        log.error("更新订单支付状态返回空" + wxPayNotifyDO.getOrderCode());
                        return;
                    }
                } catch (Exception e) {
                    errorMsg = "更新订单支付状态异常";
                    wxPayService.updateWxPayNotifyStatus(tradeNo,status,errorMsg);
                    getResponse().getWriter().write(PayCommonUtil.setXML("FAIL", errorMsg));
                    log.error("更新订单支付状态异常" + wxPayNotifyDO.getOrderCode(),e);
                    return;
                }

                //10.更新支付记录状态
                wxPayService.updateWxPayRecordStatus(tradeNo,WxPayRecordStatusEnum.PAY.code,WxPayRecordStatusEnum.PAY.desc);
                wxPayService.updateWxPayNotifyStatus(tradeNo,WxPayNotifyStatusEnum.SUCCESS.code,WxPayNotifyStatusEnum.SUCCESS.desc);
                getResponse().getWriter().write(PayCommonUtil.setXML("SUCCESS", "OK"));
                log.info("异步微信支付结果通知成功，订单支付成功!" + tradeNo);
            } else {
                errorMsg = noticeDatas.get("err_code_des");
                wxPayService.updateWxPayNotifyStatus(tradeNo,status,errorMsg);
                getResponse().getWriter().write(PayCommonUtil.setXML("FAIL", errorMsg));
                log.error("异步微信支付结果通知失败：" + errorMsg);
                return;
            }
        } else {
            errorMsg = noticeDatas.get("return_msg");
            wxPayService.updateWxPayNotifyStatus(tradeNo,status,errorMsg);
            getResponse().getWriter().write(PayCommonUtil.setXML("FAIL", errorMsg));
            log.error("异步微信支付结果通知失败：" + errorMsg);
            return;
        }
    }

    /**
     * 检查微信版本是否支持微信支付
     *
     * @author guonanjun
     * @createDate 2016年3月24日 下午12:02:17
     *
     * @return	如果不支持微信支付返回错误信息，否则返回null
     */
    private String checkWechatVersion() {

        String errorMsg = null;
        // 获取浏览器类型
        String ua = CommonUtil.getUserAgent(this.getRequest());
        if (StringUtils.isBlank(ua)) {	//无法获取浏览器类型
            errorMsg = "请在微信中打开该链接进行支付！";
            return errorMsg;
        }
        if (ua == null || ua.indexOf("micromessenger") <= 0) {	//不是微信浏览器
            errorMsg = "请在微信中打开该链接进行支付！";
            return errorMsg;
        } else {	//微信版本低于5.0
            char wechatVersion = ua.charAt(ua.indexOf("micromessenger")+15);
            Integer version = Integer.valueOf(wechatVersion);
            if (version < 5) {
                errorMsg = "您的微信版本低于5.0无法使用微信支付,请安装最新版本！";
                return errorMsg;
            }
        }
        String openid = getCacheVisitWxOpenId();
        if (StringUtils.isBlank(openid)) {
            errorMsg = "您的登录会话已过期，请您重新登录！";
            return errorMsg;
        }
        return errorMsg;
    }
}
